#include "structwidget.h"
#include <QtXml>
#include <QtGui>

const int CURPROP_NONE=0;
const int CURPROP_CUBE=1;
const int CURPROP_ATTR=2;


const QString PNAME1=QObject::trUtf8("Логическое имя");
const QString PNAME2=QObject::trUtf8("Физическое имя");
const QString PNAME3=QObject::trUtf8("Тип");


StructWidget::StructWidget():QWidget()
{
	setupUi(this);
	_stage1 = new Stage1;
	_stage2 = new Stage2;
	_stage3 = new Stage3;
	_stage2->setAttributes(_stage1->attributes());
	_stage3->setAttributes(_stage1->attributes());
	_stage1->setTreeView(treeViewStage1);
	_stage2->setTreeWidget(treeWidgetStage2);
	_stage3->setTreeWidget(treeWidgetStage3);
	setCurrentFileName(QString());
	_current_property_type = 0;
	//treeEdit->setItemDelegate(new PropDelegate);
	connect(treeViewStage1->selectionModel(),SIGNAL(currentChanged(const QModelIndex&,const QModelIndex&)),SLOT(attrSelected(const QModelIndex&,const QModelIndex&)));
	connect(treeWidgetStage3->selectionModel(),SIGNAL(currentChanged(const QModelIndex&,const QModelIndex&)),SLOT(cubeOrAttrSelected(const QModelIndex&,const QModelIndex&)));
	connect(pbSave,SIGNAL(clicked()),SLOT(save()));
	//connect(treeEdit,SIGNAL(itemChanged(QTreeWidgetItem*,int)),SLOT(propChanged(QTreeWidgetItem*,int)));
	connect(pbNewCrd,SIGNAL(clicked()),_stage1,SLOT(slotAddAttribute()));
	connect(pbNewCube,SIGNAL(clicked()),_stage3,SLOT(slotAddCube()));

	connect(_stage3,SIGNAL(treeUpdated(int)),SLOT(dataUpdated()));
	connect(editPhisNameBD,SIGNAL( textChanged(QString)),SLOT(dataUpdated()));
	//treeEdit->setHeaderLabels(QStringList() << trUtf8("Свойство") << trUtf8("Значение"));
	properties->setColumnWidth(0,200);
	splitter_2->setSizes(QList<int>() << 1000 << 320);
	checkSendAvaliable();
}

void StructWidget::clear()
{
	_stage3->clear();
	_stage2->clear();
	_stage1->clear();	
}

void StructWidget::loadFromFile(const QString &fname)
{
	QFile file(fname);
	if(!file.open(QIODevice::ReadOnly)){
		QMessageBox::warning(0,trUtf8("Ошибка"),tr("Не могу открыть файл '%1'\nОшибка: %2").arg(file.fileName()).arg(file.error()));		
		return;		
	}
	// извлекаем данные из открытого файла
	
	QDomDocument doc;
	QString errMess;
	int col,line;
	if(!doc.setContent(&file,&errMess,&line,&col)){
		file.close();
		//if(!silent) 
		QMessageBox::warning(this,trUtf8("Ошибка"),QString("Ошибка разбора файла: '%1'\n%2\nстрока=%3 колонка=%4").arg(fname).arg(errMess).arg(line).arg(col));
		return;
	}
	setCurrentFileName(fname);
	QDomElement root;
	root = doc.firstChildElement("dbstruct");		
	if(root.isNull()){
		QMessageBox::warning(this,trUtf8("Ошибка"),trUtf8("Неверный формат файла проекта. Не найден тег dbstruct"));
		return;
	}
	loadStruct(root);
	file.close();
}

void StructWidget::loadStruct(const QDomElement &root)
{
	clear();
	loadDatabaseProps(root);
	_stage1->load(root.firstChildElement("stage1"));
	_stage2->load(root.firstChildElement("stage2"));
	_stage3->load(root.firstChildElement("stage3"));
}

void StructWidget::loadDatabaseProps(const QDomElement &root)
{
	QDomElement elem = root.firstChildElement("props");
	if(!elem.isNull()){	
		editLogicNameBD->setText(elem.attribute("alias"));
		editPhisNameBD->setText(elem.attribute("name"));
	}
}

QString StructWidget::databaseName()
{
	return editPhisNameBD->text();
}

void StructWidget::addProperty(const QString &name,const QString &value,int type, const QStringList &xvalues)
{
	QWidget *edit=0;
	switch(type){		
		case PROP_COMBO:
			edit = new QComboBox();
			((QComboBox*)edit)->insertItems(0,xvalues);
			((QComboBox*)edit)->setCurrentIndex(((QComboBox*)edit)->findText(value));
			connect(edit,SIGNAL(currentIndexChanged ( const QString &)),SLOT(propChanged(const QString&)));
			break;
		default:
			edit = new QLineEdit(value);
			connect(edit,SIGNAL(textChanged ( const QString &)),SLOT(propChanged(const QString&)));
			break;
	}
	edit->setProperty("name",name);
	properties->addPropertyWidget(name,edit);
}

void StructWidget::attrSelected( const QModelIndex & current, const QModelIndex & previous )
{
	showAttributeProperties(((AttributesModel*)treeViewStage1->model())->attrFromIndex(current));	
}

void StructWidget::showAttributeProperties(Attribute *attr)
{
	if(!attr) return;
	//qDebug() << Q_FUNC_INFO << "start";
	clearProperties();
	_current_property_type = CURPROP_ATTR;
	_currentAttr = attr;
	addProperty(PNAME1,attr->alias());
	addProperty(PNAME2,attr->name());
	addProperty(PNAME3,attr->type(),PROP_COMBO,QStringList() << trUtf8("string")<< trUtf8("double"));
	//qDebug() << Q_FUNC_INFO << "end";
}

void StructWidget::clearProperties()
{
	properties->clear();
	/*treeEdit->clear();
	treeEdit->setColumnCount(2);
	treeEdit->setColumnWidth(0,150);	*/
}

void StructWidget::cubeOrAttrSelected(const QModelIndex &current,const QModelIndex&)
{	
	QTreeWidgetItem *item = treeWidgetStage3->currentItem();//treeWidgetStage3->itemFromIndex(current);
	_currentItem = item;
	if(item==0) return;
	//qDebug() << Q_FUNC_INFO << current.parent().isValid();
	if(!current.parent().isValid()){ // выбран гиперкуб
		item = treeWidgetStage3->topLevelItem(current.row());
		_current_property_type = CURPROP_CUBE;
		//_currentItem = item;
		clearProperties();
		addProperty(PNAME1,item->text(0));
		addProperty(PNAME2,item->data(0,Qt::UserRole).toString());
	}
	else{
		
		if(item){
			Attribute *attr=_stage1->attributes()->attribute(item->data(0,Qt::UserRole).toInt());
			showAttributeProperties(attr);
		}
	}
}

void StructWidget::save()
{
	QString fname = _currentFileName;
	if(fname.isNull()){
		fname = QFileDialog::getSaveFileName(this,trUtf8("Сохранить файл с проектом структуры базы данных"),QSettings().value("last_file_project_dbstruct","").toString(),STRUCT_PROJ_FILENAME_FILTER);
		if(fname.isEmpty()){
			return;
		}
	}
	saveInFile(fname);
}

void StructWidget::setCurrentFileName(const QString &fname)
{
	_currentFileName = fname;
	labFileName->setText(_currentFileName);
}

void StructWidget::saveInFile(const QString &fname)
{
	
	QFile file(fname);
	if(!file.open(QIODevice::WriteOnly)){
		QMessageBox::warning(0,trUtf8("Ошибка"),trUtf8("Файл не создан '%1'\nКод ошибки: %2").arg(file.fileName()).arg(file.error()));		
		return;
	}
	setCurrentFileName(fname);
	// запись структуры в файл
	QDomDocument doc;
	QDomElement node;
	QDomElement root;
	root = doc.createElement("dbstruct");
	// свойства БД
	node = doc.createElement("props");
	node.setAttribute("name",databaseName());
	node.setAttribute("alias",databaseAlias());
	root.appendChild(node);
	// структура БД
	
	root.appendChild(_stage1->xml());
	root.appendChild(_stage2->xml());
	root.appendChild(_stage3->xml());	
	
	doc.appendChild(root);

	QTextStream out(&file);
	out.setCodec("UTF-8");
	out << doc.toString();
	file.close();
	loadFromFile(fname);
	emit saved();
}

QString StructWidget::databaseAlias()
{
	return editLogicNameBD->text();	
}

void StructWidget::propChanged(const QString &value)
{
	if(sender()==0) return;
	qDebug() << Q_FUNC_INFO << "start" << value;
	QString name = sender()->property("name").toString();
	
	if(_current_property_type==CURPROP_ATTR){
		if(!_currentAttr) return;
		if(name==PNAME1){
			_currentAttr->setAlias(value);
		}
		if(name==PNAME2){
			_currentAttr->setName(value);
		}
		if(name==PNAME3){
			_currentAttr->setType(value);
		}
	}
	
	if(_current_property_type==CURPROP_CUBE){
		if(!_currentItem) return;
		if(name==PNAME1){
			_currentItem->setText(0,value);
		}
		if(name==PNAME2){
			_currentItem->setData(0,Qt::UserRole,value);
		}
	}	
	
	QApplication::processEvents();
	treeViewStage1->update();
	treeViewStage1->repaint();
	qDebug() << Q_FUNC_INFO << "end";
}

void StructWidget::on_pbSend_clicked()
{
	QDomDocument doc = serverFormat();
	_dbase->sendDatabaseStruct(doc);
	if(QMessageBox::question(this,trUtf8("Встал вопрос."),trUtf8("Структура создана. Загрузить дерево структуры базы данных?"),QMessageBox::Yes | QMessageBox::No)==QMessageBox::Yes){
		emit openBase(databaseName());		
	}
}

QDomDocument StructWidget::serverFormat()
{
	QDomDocument doc;
	QDomElement root = doc.createElement("ums");
	root = _stage3->serverFormat(databaseName(),databaseAlias());
	doc.appendChild(root);	
	return doc;
}

void StructWidget::setDatabase(UMSDatabase *db)
{
	_dbase = db;
}

void StructWidget::on_pbSaveAs_clicked()
{
	QString fname = QFileDialog::getSaveFileName(this,trUtf8("Сохранить файл с проектом структуры базы данных"),_currentFileName,STRUCT_PROJ_FILENAME_FILTER);
	if(fname.isEmpty()) return;
	saveInFile(fname);	
}

void StructWidget::checkSendAvaliable()
{
    pbSend->setEnabled( !editPhisNameBD->text().isEmpty() && gipercubesCount()>0 );
}

int StructWidget::gipercubesCount()
{
	int count=0;
	QDomElement elem = _stage3->xml();
	QDomElement item = elem.firstChildElement("item");
	while(!item.isNull()){
		if(item.attribute("order")=="0") count++;
		item = item.nextSiblingElement("item");
	}
	return count;
}

void StructWidget::dataUpdated()
{
	checkSendAvaliable();
}

void StructWidget::loadFromServer(const QString &dbname)
{
	//qDebug() << _dbase->databaseStruct(dbname).toString();
	//QDomElement dbstruct = convertFromServerFormat(_dbase->databaseStruct(dbname).firstChildElement("ums"));
	loadStruct(convertFromServerFormat(_dbase->databaseStruct(dbname).firstChildElement("ums")));
}


QDomElement StructWidget::createTextElement(const QString &teg,const QString &value)
{
		QDomDocument doc;
		QDomElement elem;
		QDomText telem;
		
		elem  = doc.createElement(teg);
		telem = doc.createTextNode(value);	
		elem.appendChild(telem);
		
		return elem;
}


// извлекает список атрибутов из дерева атрибутов
// возвращает текущий новый номер для элемента (order=1 по умолчанию)
int StructWidget::getAttributeList(const QDomElement &root,QDomElement *list,int order)
{
	QDomElement attr = root.firstChildElement("attribute");
	QDomDocument doc;
	QDomElement xattr;
	while(!attr.isNull()){
		xattr = doc.createElement("attribute");
		xattr.setAttribute("name",attr.attribute("name"));
		xattr.appendChild(createTextElement("id",attr.attribute("alias")));
		xattr.appendChild(createTextElement("type",attr.attribute("type")));
		xattr.appendChild(createTextElement("order",QString::number(order++)));
		xattr.appendChild(createTextElement("length",QString("%1").arg(0)));
		xattr.appendChild(createTextElement("idconf",attr.attribute("form")));
		xattr.appendChild(createTextElement("opened",attr.attribute("open")));
		if(!hasAttribute(list,&xattr)) list->appendChild(xattr);
		else order--;
		order = getAttributeList(attr,list,order);
		attr = attr.nextSiblingElement("attribute");
	}
	return order;
}

//
//  загрузка в серверный формат - на вход ветвь с тегом ums, на выход "родной" формат с dbstruct
//
QDomElement StructWidget::convertFromServerFormat(const QDomElement &ums)
{
	if(ums.isNull()) return QDomElement();

	QDomDocument doc;
	QDomElement root = doc.createElement("dbstuct");
	QDomElement props = doc.createElement("props");
	props.setAttribute("name",ums.attribute("name"));
	props.setAttribute("alias",ums.attribute("alias"));
	root.appendChild(props);
	QDomElement stage1 = doc.createElement("stage1");
	root.appendChild(stage1);
	// первая стадия - получаем список атрибутов
	QDomElement domain = ums.firstChildElement("domain");
	int order = 1;
	while(!domain.isNull()){
		//qDebug() << "stage1: processing domain" << domain.attribute("dalias");
		order = getAttributeList(domain,&stage1,order);
		domain = domain.nextSiblingElement("domain");
	}
	QDomElement attr100 = stage1.firstChildElement("attribute");
	while(!attr100.isNull()){
		//qDebug() << "order="<<attr100.firstChildElement("order").text().toInt();
		attr100 = attr100.nextSiblingElement();
	}
	// вторая стадия - сложные атрибуты (пока пустая)
	QDomElement stage;
	stage = doc.createElement("stage2");
	root.appendChild(stage);
	// третья стадия - иерархия
	stage = doc.createElement("stage3");
	root.appendChild(stage);	
	
	domain = ums.firstChildElement("domain");
	QDomElement newdomain;
	while(!domain.isNull()){
		//qDebug() << "stage3: processing domain" << domain.attribute("dalias");
		newdomain = domain.cloneNode().toElement();
		newdomain.setTagName("item");
		newdomain.setAttribute("order","0");
		newdomain.setAttribute("dalias",domain.attribute("dalias"));
		newdomain.setAttribute("name",domain.attribute("name"));	
		
		convertDomainAttributes(newdomain,stage1);
		stage.appendChild(newdomain);
		domain = domain.nextSiblingElement("domain");		
	}
	//doc.appendChild(stage1);
	//doc.appendChild(stage);
	
	//qDebug() << "doc=" << doc.toString();
	return root;	
}

//
// проверка на наличие атрибута в списке
//
bool StructWidget::hasAttribute(const QDomElement *list,const QDomElement *xattr)
{
	QDomElement attr = list->firstChildElement("attribute");
	while(!attr.isNull()){
		if(xattr->attribute("name")==attr.attribute("name")) return true;
		attr = attr.nextSiblingElement("attribute");
	}
	return false;
}
//
//	преобразует иерархию атрибутов в домене
//
void StructWidget::convertDomainAttributes(const QDomElement &xroot,const QDomElement &stage1)
{
	QDomElement attr = xroot.firstChildElement("attribute");
	while(!attr.isNull()){
		QString aname = attr.attribute("name");
		int nom = attributeOrder(aname,stage1);
		if(nom == 0) {
			qDebug("convertDomainAttributes: nom==0");
			break;			
		}
		
		attr.setTagName("item");
		attr.setAttribute("order",QString::number(nom));
		convertDomainAttributes(attr,stage1);
		attr = attr.nextSiblingElement("attribute");
	}
}

//
// возвращает номер атрибута по его имени
//
int StructWidget::attributeOrder(const QString &name,const QDomElement &stage1)
{	
	QDomElement attr = stage1.firstChildElement("attribute");
	int nom = 0;
	while(!attr.isNull()){

		if(name == attr.attribute("name")){
			nom = attr.firstChildElement("order").text().toInt();
			qDebug() << "nom found" << nom;
			break;
		}
		attr = attr.nextSiblingElement("attribute");
	}
	return nom;
}
